﻿using FySystem.Converter;
using FySystem.Logs;
using FySystem.Net.FyNetConnection.Src;
using System.Buffers;
using System.Collections.Concurrent;
using System.Net.Sockets;

namespace IOT
{
    /// <summary>
    /// 采集器和设备一对一的
    /// </summary>
    public class IOTStateObject<T> : StateObjectBase
    {
        private bool _needToSend = false;
        private DateTime _now = DateTime.Now;
        private IOTMessage<T> _msg = null;

        /// <summary>
        /// 连接IP地址
        /// </summary>
        public string IP { get; set; }

        /// <summary>
        /// 逆变器型号，组包时需要用
        /// </summary>
        public string Model { get; set; }

        /// <summary>
        /// 采集器序列号
        /// </summary>
        public string GatewaySN { get; set; }

        /// <summary>
        /// 实时数据
        /// </summary>
        public T Real { get; set; }

        /// <summary>
        /// 数据发送队列，将要发送的数据放到这个队列中，由内部一个线程来监测并在合适的时间将数据发送出去
        /// 据说是线程安全的队列，可以节省因锁而消耗的资源，试验一下
        /// </summary>
        public ConcurrentQueue<IOTMessage<T>> SendQueue { get; } = new ConcurrentQueue<IOTMessage<T>>();

        /// <summary>
        /// 上次发送时间，由内部维护
        /// </summary>
        internal DateTime LastSendTime = DateTime.MinValue;

        /// <summary>
        /// 每两次数据发送之间的时间间隔，有些采集器短时间内连续收到数据会忽略掉后续的数据，所以只能间隔一段时间后再发送
        /// </summary>
        public int SendDeltaTime { get; set; }

        /// <summary>
        /// 当前正在发送的数据包
        /// </summary>
        public IOTMessage<T> CurrentPack { get; private set; }

        /// <summary>
        /// 内部定时执行该函数来发送数据
        /// </summary>
        internal void Send(int timeOut, int sendDeltaTime)
        {
            try
            {
                _now = DateTime.Now;        //初始化当前时间
                if ((_now - LastSendTime).TotalMilliseconds < sendDeltaTime)
                    return;

                _needToSend = false;        //初始化为假
                _msg = null;

                if (CurrentPack == null)
                    _needToSend = true;              //为空表示这个是新的连接，还没有发送过数据
                else
                {
                    if (CurrentPack.NeedReply)
                    {
                        //如果需要回复
                        if (CurrentPack.IsReply == true)
                        {
                            _needToSend = true;
                        }
                    }
                    else
                    {
                        //如果不需要回复
                        _needToSend = true;
                    }

                    //如果上面判断下来是假，再判断是否超时，超时了就可以发
                    if (_needToSend == false)
                    {
                        if ((_now - LastSendTime).TotalSeconds >= timeOut)
                        {
                            _needToSend = true;
                            if (CurrentPack != null)
                                FyLogContextV2.WriteError("发送超时", NumberConversion.BytesToHexString(CurrentPack.Package, 0, CurrentPack.Package.Length, false));
                            CurrentPack = null;             //超时了就把当前包移除掉
                        }
                    }
                }

                if (_needToSend)
                {
                    LastSendTime = _now;

                    //发送下一帧，如果有的话
                    if (SendQueue.TryDequeue(out _msg))
                    {
                        CurrentPack = _msg;
                        WorkSocket.BeginSend(_msg.Package, 0, _msg.Package.Length, SocketFlags.None, null, null);
                        FyLogContextV2.WriteLog("send", NumberConversion.BytesToHexString(_msg.Package.AsSpan()));
                    }
                }
            }
            catch (Exception ex)
            {
                FyLogContextV2.WriteError("IOTStateObject.Send", ex.Message);
            }
        }
    }
}